/*
 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.xml.bind;

/**
 * Processes white space normalization.
 *
 * @since 1.0
 */
abstract class WhiteSpaceProcessor {

    // benchmarking (see test/src/ReplaceTest.java in the CVS Attic)
    // showed that this code is slower than the current code.
    //
    //    public static String replace(String text) {
    //        final int len = text.length();
    //        StringBuffer result = new StringBuffer(len);
    //
    //        for (int i = 0; i < len; i++) {
    //            char ch = text.charAt(i);
    //            if (isWhiteSpace(ch))
    //                result.append(' ');
    //            else
    //                result.append(ch);
    //        }
    //
    //        return result.toString();
    //    }

    public static String replace(String text) {
        return replace((CharSequence) text).toString();
    }

    /**
     * @since 2.0
     */
    public static CharSequence replace(CharSequence text) {
        int i = text.length() - 1;

        // look for the first whitespace char.
        while (i >= 0 && !isWhiteSpaceExceptSpace(text.charAt(i)))
            i--;

        if (i < 0)
            // no such whitespace. replace(text)==text.
            return text;

        // we now know that we need to modify the text.
        // allocate a char array to do it.
        StringBuilder buf = new StringBuilder(text);

        buf.setCharAt(i--, ' ');
        for (; i >= 0; i--)
            if (isWhiteSpaceExceptSpace(buf.charAt(i)))
                buf.setCharAt(i, ' ');

        return new String(buf);
    }

    /**
     * Equivalent of {@link String#trim()}.
     * @since 2.0
     */
    public static CharSequence trim(CharSequence text) {
        int len = text.length();
        int start = 0;

        while (start < len && isWhiteSpace(text.charAt(start)))
            start++;

        int end = len - 1;

        while (end > start && isWhiteSpace(text.charAt(end)))
            end--;

        if (start == 0 && end == len - 1)
            return text; // no change
        else
            return text.subSequence(start, end + 1);
    }

    public static String collapse(String text) {
        return collapse((CharSequence) text).toString();
    }

    /**
     * This is usually the biggest processing bottleneck.
     *
     * @since 2.0
     */
    public static CharSequence collapse(CharSequence text) {
        int len = text.length();

        // most of the texts are already in the collapsed form.
        // so look for the first whitespace in the hope that we will
        // never see it.
        int s = 0;
        while (s < len) {
            if (isWhiteSpace(text.charAt(s)))
                break;
            s++;
        }
        if (s == len)
            // the input happens to be already collapsed.
            return text;

        // we now know that the input contains spaces.
        // let's sit down and do the collapsing normally.

        StringBuilder result = new StringBuilder(len /*allocate enough size to avoid re-allocation*/);

        if (s != 0) {
            for (int i = 0; i < s; i++)
                result.append(text.charAt(i));
            result.append(' ');
        }

        boolean inStripMode = true;
        for (int i = s + 1; i < len; i++) {
            char ch = text.charAt(i);
            boolean b = isWhiteSpace(ch);
            if (inStripMode && b)
                continue; // skip this character

            inStripMode = b;
            if (inStripMode)
                result.append(' ');
            else
                result.append(ch);
        }

        // remove trailing whitespaces
        len = result.length();
        if (len > 0 && result.charAt(len - 1) == ' ')
            result.setLength(len - 1);
        // whitespaces are already collapsed,
        // so all we have to do is to remove the last one character
        // if it's a whitespace.

        return result;
    }

    /**
     * Returns true if the specified string is all whitespace.
     */
    public static final boolean isWhiteSpace(CharSequence s) {
        for (int i = s.length() - 1; i >= 0; i--)
            if (!isWhiteSpace(s.charAt(i)))
                return false;
        return true;
    }

    /** returns true if the specified char is a white space character. */
    public static final boolean isWhiteSpace(char ch) {
        // most of the characters are non-control characters.
        // so check that first to quickly return false for most of the cases.
        if (ch > 0x20)
            return false;

        // other than we have to do four comparisons.
        return ch == 0x9 || ch == 0xA || ch == 0xD || ch == 0x20;
    }

    /**
     * Returns true if the specified char is a white space character
     * but not 0x20.
     */
    protected static final boolean isWhiteSpaceExceptSpace(char ch) {
        // most of the characters are non-control characters.
        // so check that first to quickly return false for most of the cases.
        if (ch >= 0x20)
            return false;

        // other than we have to do four comparisons.
        return ch == 0x9 || ch == 0xA || ch == 0xD;
    }
}
